Tidyverse

tidyverse.org defines Tidyverse as

The tidyverse is an opinionated collection of R packages designed for data science. All packages share an underlying design philosophy, grammar, and data structures.

library(tidyverse)
library(dplyr)
library(scater)

We will use single-cell RNA sequencing on 6826 stem cells from Chronic myelomonocytic leukaemia (CMML) patients and healthy controls using the droplet-based, ultra-high-throughput 10x platform. We found substantial inter and intra-patient heterogeneity, with CMML stem cells displaying distinctive transcriptional programs. Compared with normal controls, CMML stem cells exhibited transcriptomes characterized by increased expression of myeloid-lineage and cell cycle genes, and lower expression of genes selectively expressed by normal haematopoietic stem cells.

sce <- readRDS('sce.rds')
sce
class: SingleCellExperiment 
dim: 12695 6826 
metadata(0):
assays(3): counts logcounts norm_exprs
rownames(12695): FO538757.2 AP006222.2 ... AC004556.1 AC240274.1
rowData names(12): id symbol ... total_counts log10_total_counts
colnames(6826): AAACCTGCACCGATAT-1 AAACGGGCACGACTCG-1 ... TTTGGTTTCATCTGCC-11 TTTGTCAGTAGGAGTC-11
colData names(59): barcode Sample ... sizeFactor cellType
reducedDimNames(1): tSNE
altExpNames(0):

Tibble

  • Tibbles are data-frames
  • tibble() does much less: it never changes the type of the inputs (e.g. it never converts strings to factors!)
  • it never changes the names of variables, and it never creates row names.
  • tibble can have column names that are not valid R variable names, aka non-syntactic names.
tb <- tibble(
  `:)` = "smile", 
  ` ` = "space",
  `2000` = "number"
)
tb

Pipe %>%

Pipe %>% passes the output from one stage to the other.

tbl_df(colData(sce))
names(colData(sce))
 [1] "barcode"                                        "Sample"                                        
 [3] "total_features"                                 "log10_total_features"                          
 [5] "pct_counts_top_50_features"                     "pct_counts_top_100_features"                   
 [7] "pct_counts_top_200_features"                    "pct_counts_top_500_features"                   
 [9] "total_features_endogenous"                      "log10_total_features_endogenous"               
[11] "pct_counts_top_50_features_endogenous"          "pct_counts_top_100_features_endogenous"        
[13] "pct_counts_top_200_features_endogenous"         "pct_counts_top_500_features_endogenous"        
[15] "total_features_feature_control"                 "log10_total_features_feature_control"          
[17] "total_features_Mt"                              "log10_total_features_Mt"                       
[19] "is_cell_control"                                "total_features_by_counts"                      
[21] "log10_total_features_by_counts"                 "total_counts"                                  
[23] "log10_total_counts"                             "pct_counts_in_top_50_features"                 
[25] "pct_counts_in_top_100_features"                 "pct_counts_in_top_200_features"                
[27] "pct_counts_in_top_500_features"                 "total_features_by_counts_endogenous"           
[29] "log10_total_features_by_counts_endogenous"      "total_counts_endogenous"                       
[31] "log10_total_counts_endogenous"                  "pct_counts_endogenous"                         
[33] "pct_counts_in_top_50_features_endogenous"       "pct_counts_in_top_100_features_endogenous"     
[35] "pct_counts_in_top_200_features_endogenous"      "pct_counts_in_top_500_features_endogenous"     
[37] "total_features_by_counts_feature_control"       "log10_total_features_by_counts_feature_control"
[39] "total_counts_feature_control"                   "log10_total_counts_feature_control"            
[41] "pct_counts_feature_control"                     "pct_counts_in_top_50_features_feature_control" 
[43] "pct_counts_in_top_100_features_feature_control" "pct_counts_in_top_200_features_feature_control"
[45] "pct_counts_in_top_500_features_feature_control" "total_features_by_counts_Mt"                   
[47] "log10_total_features_by_counts_Mt"              "total_counts_Mt"                               
[49] "log10_total_counts_Mt"                          "pct_counts_Mt"                                 
[51] "pct_counts_in_top_50_features_Mt"               "pct_counts_in_top_100_features_Mt"             
[53] "pct_counts_in_top_200_features_Mt"              "pct_counts_in_top_500_features_Mt"             
[55] "CellCycle"                                      "Cluster"                                       
[57] "Phase"                                          "sizeFactor"                                    
[59] "cellType"                                      
tbl_df(colData(sce)) %>%
  group_by(Sample) %>%
  summarise(
    total.features = mean(total_features),
    total.counts = mean(total_counts)
  )
`summarise()` ungrouping output (override with `.groups` argument)

dplyr - Functions as verbs.

The most useful

  • select(): select columns
  • mutate(): create new variables, change existing
  • filter(): subset your data by some criterion
  • summarize(): summarize your data in some way
  • group_by(): group your data by a variable
  • slice(): grab specific rows
  • select(): select an observation

Some others

  • count(): count your data
  • arrange(): arrange your data by a column or variable
  • distinct(): gather all distinct values of a variable
  • n_distinct(): count how many distinct values you have (only works with summarize)
  • n(): count how many observation you have for a subgroup
  • sample_n(): Grab an N sample of your data
  • ungroup(): ungroup grouped data by a variable
  • top_n(): get the top N number of entries from a data frame

__ To make it easier we copy the metadata for our SingleCellExperiment object sce to d

d <- tbl_df(colData(sce))

Select : To select collumns

select(d, Sample, Cluster, cellType)
d %>% 
  select(Sample, Cluster, cellType)

Filter : To select rows

d %>% 
  filter(cellType == "HSC")
d %>% 
  select(barcode, Sample, total_features, cellType, Cluster) %>%
  filter(Sample == "BC572")
NA
d %>% 
  filter(cellType == "Erythrocytes", pct_counts_Mt > 1.5) %>% 
  select(barcode, Sample, pct_counts_Mt, cellType, Cluster)

Mutate:

To create new variables in the data table:

d_exp <- d
d_exp <- cbind(d_exp, t(logcounts(sce)[c('KLF4','RUNX1','EGR1'),]))
d_exp
d_exp %>% 
  mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>%
  select(barcode, Sample, cellType, Klf4Diff)

Arrange:

To order the data by a particular variable:

d_exp %>% 
  mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>% 
  arrange(desc(Klf4Diff)) %>% 
  slice(1)

Slice:

To slice your data by rows:

# The top 5 goleadas?
d_exp %>% 
  mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>% 
  arrange(desc(Klf4Diff)) %>% 
  slice(1:5)  # slice_max here would also do the trick
# The top 5 goleadas?
d_exp %>% 
  mutate(Klf4Diff = (KLF4 - RUNX1)) %>% 
  arrange(desc(Klf4Diff)) %>% 
  select(barcode, Sample, cellType, Klf4Diff) %>%
  slice_min(Klf4Diff, n = 5)

Group by + sumarize : forget about loops

First: group by a particular variables Second: summarize the data with new statistics. Summarize: Turn many rows into one.

Examples:

  • min(x) - minimum value of vector x.
  • max(x) - maximum value of vector x.
  • mean(x) - mean value of vector x.
  • median(x) - median value of vector x.
  • quantile(x, p) - pth quantile of vector x.
  • sd(x) - standard deviation of vector x.
  • var(x) - variance of vector x.
  • IQR(x) - Inter Quartile Range (IQR) of vector x.
  • diff(range(x)) - total range of vector x.
d %>% 
  group_by(cellType) %>% 
  summarise(mean_total_counts = mean(total_counts, na.rm = TRUE), sd_total_counts = sd(total_counts), 
     mean_pct_Mt_count = mean(pct_counts_Mt), count = n()) %>% 
  #ungroup() %>% 
  slice_max(., n=20, order_by = mean_total_counts)  # note here, it does 
`summarise()` ungrouping output (override with `.groups` argument)

Note: mutate() either changes an existing column or adds a new one. summarise() calculates a single value (per group). As you can see, in the first example, new column is added.

d %>% 
count(Sample, cellType)

Plotting in R using ggplot2

GGPlot2 is a powerful and a flexible R package, implemented by Hadley Wickham, for producing elegant graphics piece by piece (Wickham et al. 2017).

The gg in ggplot2 means Grammar of Graphics, a graphic concept which describes plots by using a “grammar”. According to the ggplot2 concept, a plot can be divided into different fundamental parts:

Plot = Data + Aesthetics + Geometry

  1. Data: a data frame
  2. Aesthetics: used to indicate the x and y variables. It can be also used to control the color, the size and the shape of points, etc…..
  3. Geometry: corresponds to the type of graphics (scatter plot, histogram, box plot, line plot, ….)
  4. additional layers for customization — title, labels, axis, etc.

First plotting

The main function in the ggplot2 package is ggplot(), which can be used to initialize the plotting system with data and x/y variables.

For example, the following R code takes the KLF4 and RUNX1 data set to initialize the ggplot and then a layer (geom_point()) is added onto the ggplot to create a scatter plot of x = KLF4 by y = RUNX1:

  1. Data= d_exp
  2. Aesthetic=: aes(x=KLF4, y=RUNX1)
  3. Geometry= geom_point()
ggplot(d_exp, aes(x=KLF4, y=RUNX1))

ggplot(d_exp, aes(x=KLF4, y=RUNX1)) + geom_point()

ggplot(d_exp, aes(x=KLF4, y=RUNX1)) + geom_point(size = 1.2, color = "steelblue", shape = 21)

It’s also possible to control points shape and color by a grouping variable (here, Sample). For example, in the code below, we map points color and shape to the datasets grouping variable.

Note that, a ggplot can be holded in a variable, say p, to be printed later

# Control points color by groups
ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
  geom_point(aes(color = Sample))


# Change the default color manually.
# Use the scale_color_manual() function
p <- ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
  geom_point(aes(color = Sample))+
  scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97"))
print(p)

GGPlot theme

Note that, the default theme of ggplots is theme_gray() (or theme_grey()), which is theme with grey background and white grid lines. More themes are available for professional presentations or publications. These include: theme_bw(), theme_classic() and theme_minimal().

To change the theme of a given ggplot (p), use this: p + theme_classic().

p <- ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
  geom_point(aes(color = Sample))+
  scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97"))
p <- p + theme_classic()
print(p)

df <- reducedDim(sce)
head(df)
                       [,1]     [,2]
AAACCTGCACCGATAT-1 31.75737 41.45222
AAACGGGCACGACTCG-1 39.95130 44.24846
AAAGCAATCCTAAGTG-1 35.30692 40.31110
AAAGTAGGTGATGATA-1 37.10719 43.00212
AAAGTAGTCTCGCTTG-1 40.69935 44.52304
AACACGTGTTGGTAAA-1 38.65847 31.53103

Adding layers to ggplot, Lines (Prediction Line)

A plot constructed with ggplot can have more than one geom. In that case the mappings established in the ggplot() call are plot defaults that can be added to or overridden. Our plot could use a regression line:

d_exp$pred.SC <- predict(lm(RUNX1 ~ KLF4, data = d_exp))

ggplot(d_exp, aes(x = KLF4, y = RUNX1)) + 
  geom_point(aes(color = Sample)) +
  geom_line(aes(y = pred.SC)) +
  theme_classic()

Title, xlab & ylab

df <- as.data.frame(reducedDim(sce))
df$Sample <- colData(sce)$Sample
p <- ggplot(df, aes(x=V1, y=V2))+
  geom_point(size = 0.4, aes(color = Sample))+
  scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) + 
  ggtitle('t-SNE plot for Samples') + 
  xlab('tSNE-1') + 
  ylab('tSNE-2') + 
  theme_classic()
print(p)

df <- as.data.frame(reducedDim(sce))
df$Sample <- colData(sce)$Sample
p <- ggplot(df, aes(x=V1, y=V2))+
  geom_point(size = 0.4, aes(color = Sample))+
  scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) + 
  ggtitle('t-SNE plot for Samples') + 
  xlab('tSNE-1') + 
  ylab('tSNE-2') + 
  theme_classic() + 
  guides(colour = guide_legend(override.aes = list(size=4)))

p

Histogram

ggplot(d_exp, aes(x=total_counts)) + geom_histogram() + theme_classic() 

Density plot

df <- data.frame(x=log10(sce$total_counts+1), Sample = sce$Sample)
ggplot(df,
       aes(x = x, fill = as.factor(Sample))) + 
       geom_density(alpha = 0.5) +
       labs(x = expression('log'[10]*'(Library Size)'), title = "Total reads density", fill = "Sample") + 
       theme_classic(base_size = 14) + # Setting the base size text for plots
       scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) # Need to set fill manual

Facet

df <- data.frame(x=log10(sce$total_counts+1), Sample = sce$Sample)
ggplot(df,
       aes(x = x, fill = as.factor(Sample))) + 
       geom_density(alpha = 0.5) +
       labs(x = expression('log'[10]*'(Library Size)'), title = "Total reads density", fill = "Sample") + 
       theme_classic(base_size = 14) + # Setting the base size text for plots
       scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) +  # Need to set fill manual 
  facet_wrap(~Sample)

Statistical Transformations

Statistical Transformations

Some plot types (such as scatterplots) do not require transformations–each point is plotted at x and y coordinates equal to the original value. Other plots, such as boxplots, histograms, prediction lines etc. require statistical transformations:

  • for a smoother smother the y values must be transformed into predicted values

d_exp$pred.SC <- predict(lm(RUNX1 ~ KLF4, data = d_exp))

ggplot(d_exp, aes(x = KLF4, y = RUNX1)) + 
  geom_point(aes(color = Sample)) +
  geom_smooth() +
  theme_classic()

NA
NA
d_exp$pred.SC <- predict(lm(RUNX1 ~ KLF4, data = d_exp))

ggplot(d_exp, aes(x = KLF4, y = RUNX1)) + 
  geom_point(aes(color = Sample)) +
  geom_smooth(method = "lm") +
  theme_classic()

LS0tCnRpdGxlOiAiVGlkeXZlcnNlICYgZ2dwbG90MiAtIElDRCBCb290Y2FtcCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgoKCiMgVGlkeXZlcnNlCnRpZHl2ZXJzZS5vcmcgZGVmaW5lcyBUaWR5dmVyc2UgYXMKCj4gVGhlIHRpZHl2ZXJzZSBpcyBhbiBvcGluaW9uYXRlZCBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgZGVzaWduZWQgZm9yIGRhdGEgc2NpZW5jZS4gQWxsIHBhY2thZ2VzIHNoYXJlIGFuIHVuZGVybHlpbmcgZGVzaWduIHBoaWxvc29waHksIGdyYW1tYXIsIGFuZCBkYXRhIHN0cnVjdHVyZXMuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2NhdGVyKQpgYGAKCldlIHdpbGwgdXNlIHNpbmdsZS1jZWxsIFJOQSBzZXF1ZW5jaW5nIG9uIDY4MjYgc3RlbSBjZWxscyBmcm9tIENocm9uaWMgbXllbG9tb25vY3l0aWMgbGV1a2FlbWlhIChDTU1MKSBwYXRpZW50cyBhbmQgaGVhbHRoeSBjb250cm9scyB1c2luZyB0aGUgZHJvcGxldC1iYXNlZCwgdWx0cmEtaGlnaC10aHJvdWdocHV0IDEweCBwbGF0Zm9ybS4gV2UgZm91bmQgc3Vic3RhbnRpYWwgaW50ZXIgYW5kIGludHJhLXBhdGllbnQgaGV0ZXJvZ2VuZWl0eSwgd2l0aCBDTU1MIHN0ZW0gY2VsbHMgZGlzcGxheWluZyBkaXN0aW5jdGl2ZSB0cmFuc2NyaXB0aW9uYWwgcHJvZ3JhbXMuIENvbXBhcmVkIHdpdGggbm9ybWFsIGNvbnRyb2xzLCBDTU1MIHN0ZW0gY2VsbHMgZXhoaWJpdGVkIHRyYW5zY3JpcHRvbWVzIGNoYXJhY3Rlcml6ZWQgYnkgaW5jcmVhc2VkIGV4cHJlc3Npb24gb2YgbXllbG9pZC1saW5lYWdlIGFuZCBjZWxsIGN5Y2xlIGdlbmVzLCBhbmQgbG93ZXIgZXhwcmVzc2lvbiBvZiBnZW5lcyBzZWxlY3RpdmVseSBleHByZXNzZWQgYnkgbm9ybWFsIGhhZW1hdG9wb2lldGljIHN0ZW0gY2VsbHMuIAoKYGBge3J9CnNjZSA8LSByZWFkUkRTKCdzY2UucmRzJykKc2NlCmBgYAoKIyMgVGliYmxlCi0gVGliYmxlcyBhcmUgZGF0YS1mcmFtZXMKLSBgdGliYmxlKClgIGRvZXMgbXVjaCBsZXNzOiBpdCBuZXZlciBjaGFuZ2VzIHRoZSB0eXBlIG9mIHRoZSBpbnB1dHMgKGUuZy4gaXQgbmV2ZXIgY29udmVydHMgc3RyaW5ncyB0byBmYWN0b3JzISkKLSBpdCBuZXZlciBjaGFuZ2VzIHRoZSBuYW1lcyBvZiB2YXJpYWJsZXMsIGFuZCBpdCBuZXZlciBjcmVhdGVzIHJvdyBuYW1lcy4KLSB0aWJibGUgY2FuIGhhdmUgY29sdW1uIG5hbWVzIHRoYXQgYXJlIG5vdCB2YWxpZCBSIHZhcmlhYmxlIG5hbWVzLCBha2EgKm5vbi1zeW50YWN0aWMqIG5hbWVzLgpgYGB7cn0KdGIgPC0gdGliYmxlKAogIGA6KWAgPSAic21pbGUiLCAKICBgIGAgPSAic3BhY2UiLAogIGAyMDAwYCA9ICJudW1iZXIiCikKdGIKYGBgCgojIyBQaXBlIGAlPiVgClBpcGUgYCU+JWAgcGFzc2VzIHRoZSBvdXRwdXQgZnJvbSBvbmUgc3RhZ2UgdG8gdGhlIG90aGVyLgpgYGB7cn0KdGJsX2RmKGNvbERhdGEoc2NlKSkKYGBgCmBgYHtyfQpuYW1lcyhjb2xEYXRhKHNjZSkpCmBgYAoKCmBgYHtyfQp0YmxfZGYoY29sRGF0YShzY2UpKSAlPiUKICBncm91cF9ieShTYW1wbGUpICU+JQogIHN1bW1hcmlzZSgKICAgIHRvdGFsLmZlYXR1cmVzID0gbWVhbih0b3RhbF9mZWF0dXJlcyksCiAgICB0b3RhbC5jb3VudHMgPSBtZWFuKHRvdGFsX2NvdW50cykKICApCmBgYAoKIyMgYGRwbHlyYCAtIEZ1bmN0aW9ucyBhcyB2ZXJicy4KCl9fVGhlIG1vc3QgdXNlZnVsX18KCi0gYHNlbGVjdCgpYDogc2VsZWN0IGNvbHVtbnMKLSBgbXV0YXRlKClgOiBjcmVhdGUgbmV3IHZhcmlhYmxlcywgY2hhbmdlIGV4aXN0aW5nCi0gYGZpbHRlcigpYDogc3Vic2V0IHlvdXIgZGF0YSBieSBzb21lIGNyaXRlcmlvbgotIGBzdW1tYXJpemUoKWA6IHN1bW1hcml6ZSB5b3VyIGRhdGEgaW4gc29tZSB3YXkKLSBgZ3JvdXBfYnkoKWA6IGdyb3VwIHlvdXIgZGF0YSBieSBhIHZhcmlhYmxlCi0gYHNsaWNlKClgOiBncmFiIHNwZWNpZmljIHJvd3MKLSBgc2VsZWN0KClgOiBzZWxlY3QgYW4gb2JzZXJ2YXRpb24KCl9fU29tZSBvdGhlcnNfXwoKLSBgY291bnQoKWA6IGNvdW50IHlvdXIgZGF0YQotIGBhcnJhbmdlKClgOiBhcnJhbmdlIHlvdXIgZGF0YSBieSBhIGNvbHVtbiBvciB2YXJpYWJsZQotIGBkaXN0aW5jdCgpYDogZ2F0aGVyIGFsbCBkaXN0aW5jdCB2YWx1ZXMgb2YgYSB2YXJpYWJsZQotIGBuX2Rpc3RpbmN0KClgOiBjb3VudCBob3cgbWFueSBkaXN0aW5jdCB2YWx1ZXMgeW91IGhhdmUgKG9ubHkgd29ya3Mgd2l0aCBzdW1tYXJpemUpCi0gYG4oKWA6IGNvdW50IGhvdyBtYW55IG9ic2VydmF0aW9uIHlvdSBoYXZlIGZvciBhIHN1Ymdyb3VwCi0gYHNhbXBsZV9uKClgOiBHcmFiIGFuIE4gc2FtcGxlIG9mIHlvdXIgZGF0YQotIGB1bmdyb3VwKClgOiB1bmdyb3VwIGdyb3VwZWQgZGF0YSBieSBhIHZhcmlhYmxlCi0gYHRvcF9uKGApOiBnZXQgdGhlIHRvcCBOIG51bWJlciBvZiBlbnRyaWVzIGZyb20gYSBkYXRhIGZyYW1lCgpfXyBUbyBtYWtlIGl0IGVhc2llciB3ZSBjb3B5IHRoZSBtZXRhZGF0YSBmb3Igb3VyIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGBzY2VgIHRvIGQKCmBgYHtyfQpkIDwtIHRibF9kZihjb2xEYXRhKHNjZSkpCmBgYAoKIyMjIGBTZWxlY3RgIDogVG8gc2VsZWN0IGNvbGx1bW5zCmBgYHtyfQpzZWxlY3QoZCwgU2FtcGxlLCBDbHVzdGVyLCBjZWxsVHlwZSkKYGBgCgpgYGB7cn0KZCAlPiUgCiAgc2VsZWN0KFNhbXBsZSwgQ2x1c3RlciwgY2VsbFR5cGUpCmBgYAoKIyMjIGBGaWx0ZXJgIDogVG8gc2VsZWN0IHJvd3MKYGBge3J9CmQgJT4lIAogIGZpbHRlcihjZWxsVHlwZSA9PSAiSFNDIikKYGBgCgpgYGB7cn0KZCAlPiUgCiAgc2VsZWN0KGJhcmNvZGUsIFNhbXBsZSwgdG90YWxfZmVhdHVyZXMsIGNlbGxUeXBlLCBDbHVzdGVyKSAlPiUKICBmaWx0ZXIoU2FtcGxlID09ICJCQzU3MiIpCgpgYGAKCmBgYHtyfQpkICU+JSAKICBmaWx0ZXIoY2VsbFR5cGUgPT0gIkVyeXRocm9jeXRlcyIsIHBjdF9jb3VudHNfTXQgPiAxLjUpICU+JSAKICBzZWxlY3QoYmFyY29kZSwgU2FtcGxlLCBwY3RfY291bnRzX010LCBjZWxsVHlwZSwgQ2x1c3RlcikKYGBgCgojIyMgYE11dGF0ZWA6IApUbyBjcmVhdGUgbmV3IHZhcmlhYmxlcyBpbiB0aGUgZGF0YSB0YWJsZToKYGBge3J9CmRfZXhwIDwtIGQKZF9leHAgPC0gY2JpbmQoZF9leHAsIHQobG9nY291bnRzKHNjZSlbYygnS0xGNCcsJ1JVTlgxJywnRUdSMScpLF0pKQpgYGAKCmBgYHtyfQpkX2V4cApgYGAKCmBgYHtyfQpkX2V4cCAlPiUgCiAgbXV0YXRlKEtsZjREaWZmID0gYWJzKEtMRjQgLSBSVU5YMSkpICU+JQogIHNlbGVjdChiYXJjb2RlLCBTYW1wbGUsIGNlbGxUeXBlLCBLbGY0RGlmZikKYGBgCgoKCiMjIyBgQXJyYW5nZWA6IApUbyBvcmRlciB0aGUgZGF0YSBieSBhIHBhcnRpY3VsYXIgdmFyaWFibGU6CgpgYGB7cn0KZF9leHAgJT4lIAogIG11dGF0ZShLbGY0RGlmZiA9IGFicyhLTEY0IC0gUlVOWDEpKSAlPiUgCiAgYXJyYW5nZShkZXNjKEtsZjREaWZmKSkgJT4lIAogIHNsaWNlKDEpCmBgYAojIyMgYFNsaWNlYDogClRvIHNsaWNlIHlvdXIgZGF0YSBieSByb3dzOgoKYGBge3J9CiMgVGhlIHRvcCA1IGdvbGVhZGFzPwpkX2V4cCAlPiUgCiAgbXV0YXRlKEtsZjREaWZmID0gYWJzKEtMRjQgLSBSVU5YMSkpICU+JSAKICBhcnJhbmdlKGRlc2MoS2xmNERpZmYpKSAlPiUgCiAgc2xpY2UoMTo1KSAgIyBzbGljZV9tYXggaGVyZSB3b3VsZCBhbHNvIGRvIHRoZSB0cmljawpgYGAKCmBgYHtyfQojIFRoZSB0b3AgNSBnb2xlYWRhcz8KZF9leHAgJT4lIAogIG11dGF0ZShLbGY0RGlmZiA9IChLTEY0IC0gUlVOWDEpKSAlPiUgCiAgYXJyYW5nZShkZXNjKEtsZjREaWZmKSkgJT4lIAogIHNlbGVjdChiYXJjb2RlLCBTYW1wbGUsIGNlbGxUeXBlLCBLbGY0RGlmZikgJT4lCiAgc2xpY2VfbWluKEtsZjREaWZmLCBuID0gNSkKYGBgCgoKIyMjIEdyb3VwIGJ5ICsgc3VtYXJpemUgOiBmb3JnZXQgYWJvdXQgbG9vcHMKCl9fRmlyc3RfXzogZ3JvdXAgYnkgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlcwpfX1NlY29uZF9fOiBzdW1tYXJpemUgdGhlIGRhdGEgd2l0aCBuZXcgc3RhdGlzdGljcy4KX19TdW1tYXJpemVfXzogVHVybiBtYW55IHJvd3MgaW50byBvbmUuCgpFeGFtcGxlczoKCi0gbWluKHgpIC0gbWluaW11bSB2YWx1ZSBvZiB2ZWN0b3IgeC4KLSBtYXgoeCkgLSBtYXhpbXVtIHZhbHVlIG9mIHZlY3RvciB4LgotIG1lYW4oeCkgLSBtZWFuIHZhbHVlIG9mIHZlY3RvciB4LgotIG1lZGlhbih4KSAtIG1lZGlhbiB2YWx1ZSBvZiB2ZWN0b3IgeC4KLSBxdWFudGlsZSh4LCBwKSAtIHB0aCBxdWFudGlsZSBvZiB2ZWN0b3IgeC4KLSBzZCh4KSAtIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB2ZWN0b3IgeC4KLSB2YXIoeCkgLSB2YXJpYW5jZSBvZiB2ZWN0b3IgeC4KLSBJUVIoeCkgLSBJbnRlciBRdWFydGlsZSBSYW5nZSAoSVFSKSBvZiB2ZWN0b3IgeC4KLSBkaWZmKHJhbmdlKHgpKSAtIHRvdGFsIHJhbmdlIG9mIHZlY3RvciB4LgoKYGBge3J9CmQgJT4lIAogIGdyb3VwX2J5KGNlbGxUeXBlKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fdG90YWxfY291bnRzID0gbWVhbih0b3RhbF9jb3VudHMsIG5hLnJtID0gVFJVRSksIHNkX3RvdGFsX2NvdW50cyA9IHNkKHRvdGFsX2NvdW50cyksIAogICAgIG1lYW5fcGN0X010X2NvdW50ID0gbWVhbihwY3RfY291bnRzX010KSwgY291bnQgPSBuKCkpICU+JSAKICAjdW5ncm91cCgpICU+JSAKICBzbGljZV9tYXgoLiwgbj0yMCwgb3JkZXJfYnkgPSBtZWFuX3RvdGFsX2NvdW50cykgICMgbm90ZSBoZXJlLCBpdCBkb2VzIApgYGAKCl9fTm90ZTogYG11dGF0ZSgpYCBlaXRoZXIgY2hhbmdlcyBhbiBleGlzdGluZyBjb2x1bW4gb3IgYWRkcyBhIG5ldyBvbmUuIGBzdW1tYXJpc2UoKWAgY2FsY3VsYXRlcyBhIHNpbmdsZSB2YWx1ZSAocGVyIGdyb3VwKS4gQXMgeW91IGNhbiBzZWUsIGluIHRoZSBmaXJzdCBleGFtcGxlLCBuZXcgY29sdW1uIGlzIGFkZGVkLl9fCgpgYGB7cn0KZCAlPiUgCmNvdW50KFNhbXBsZSwgY2VsbFR5cGUpCmBgYAoKIyBQbG90dGluZyBpbiBSIHVzaW5nIGBnZ3Bsb3QyYAoKYEdHUGxvdDJgIGlzIGEgcG93ZXJmdWwgYW5kIGEgZmxleGlibGUgUiBwYWNrYWdlLCBpbXBsZW1lbnRlZCBieSBIYWRsZXkgV2lja2hhbSwgZm9yIHByb2R1Y2luZyBlbGVnYW50IGdyYXBoaWNzIHBpZWNlIGJ5IHBpZWNlIChXaWNraGFtIGV0IGFsLiAyMDE3KS4KClRoZSBgZ2dgIGluIGBnZ3Bsb3QyYCBtZWFucyBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhIGdyYXBoaWMgY29uY2VwdCB3aGljaCBkZXNjcmliZXMgcGxvdHMgYnkgdXNpbmcgYSDigJxncmFtbWFy4oCdLiBBY2NvcmRpbmcgdG8gdGhlIGdncGxvdDIgY29uY2VwdCwgYSBwbG90IGNhbiBiZSBkaXZpZGVkIGludG8gZGlmZmVyZW50IGZ1bmRhbWVudGFsIHBhcnRzOiAKCj4gUGxvdCA9IERhdGEgKyBBZXN0aGV0aWNzICsgR2VvbWV0cnkKCgoxLiBfX0RhdGE6X18gYSBkYXRhIGZyYW1lCjIuIF9fQWVzdGhldGljczpfXyB1c2VkIHRvIGluZGljYXRlIHRoZSB4IGFuZCB5IHZhcmlhYmxlcy4gSXQgY2FuIGJlIGFsc28gdXNlZCB0byBjb250cm9sIHRoZSBjb2xvciwgdGhlIHNpemUgYW5kIHRoZSBzaGFwZSBvZiBwb2ludHMsIGV0Y+KApi4uCjMuIF9fR2VvbWV0cnk6X18gY29ycmVzcG9uZHMgdG8gdGhlIHR5cGUgb2YgZ3JhcGhpY3MgKHNjYXR0ZXIgcGxvdCwgaGlzdG9ncmFtLCBib3ggcGxvdCwgbGluZSBwbG90LCDigKYuKQo0LiBhZGRpdGlvbmFsIGxheWVycyBmb3IgY3VzdG9taXphdGlvbiDigJQgdGl0bGUsIGxhYmVscywgYXhpcywgZXRjLgoKCiMjIEZpcnN0IHBsb3R0aW5nClRoZSBtYWluIGZ1bmN0aW9uIGluIHRoZSBgZ2dwbG90MmAgcGFja2FnZSBpcyBgZ2dwbG90KClgLCB3aGljaCBjYW4gYmUgdXNlZCB0byBpbml0aWFsaXplIHRoZSBwbG90dGluZyBzeXN0ZW0gd2l0aCBfX2RhdGFfXyBhbmQgX194L3lfXyB2YXJpYWJsZXMuCgpGb3IgZXhhbXBsZSwgdGhlIGZvbGxvd2luZyBSIGNvZGUgdGFrZXMgdGhlIGBLTEY0YCBhbmQgYFJVTlgxYCBkYXRhIHNldCB0byBpbml0aWFsaXplIHRoZSBgZ2dwbG90YCBhbmQgdGhlbiBhIGxheWVyIChnZW9tX3BvaW50KCkpIGlzIGFkZGVkIG9udG8gdGhlIGdncGxvdCB0byBjcmVhdGUgYSBzY2F0dGVyIHBsb3Qgb2YgeCA9IEtMRjQgYnkgeSA9IFJVTlgxOgoKMS4gX19EYXRhPV9fIGBkX2V4cGAKMi4gX19BZXN0aGV0aWM9Ol9fIGFlcyh4PUtMRjQsIHk9UlVOWDEpCjMuIF9fR2VvbWV0cnk9X18gYGdlb21fcG9pbnQoKWAKCmBgYHtyfQpnZ3Bsb3QoZF9leHAsIGFlcyh4PUtMRjQsIHk9UlVOWDEpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZF9leHAsIGFlcyh4PUtMRjQsIHk9UlVOWDEpKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZF9leHAsIGFlcyh4PUtMRjQsIHk9UlVOWDEpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDEuMiwgY29sb3IgPSAic3RlZWxibHVlIiwgc2hhcGUgPSAyMSkKYGBgCgpJdOKAmXMgYWxzbyBwb3NzaWJsZSB0byBjb250cm9sIHBvaW50cyBzaGFwZSBhbmQgY29sb3IgYnkgYSBncm91cGluZyB2YXJpYWJsZSAoaGVyZSwgYFNhbXBsZWApLiBGb3IgZXhhbXBsZSwgaW4gdGhlIGNvZGUgYmVsb3csIHdlIG1hcCBwb2ludHMgYGNvbG9yYCBhbmQgYHNoYXBlYCB0byB0aGUgZGF0YXNldHMgZ3JvdXBpbmcgdmFyaWFibGUuCgpOb3RlIHRoYXQsIGEgYGdncGxvdGAgY2FuIGJlIGhvbGRlZCBpbiBhIHZhcmlhYmxlLCBzYXkgYHBgLCB0byBiZSBwcmludGVkIGxhdGVyCgpgYGB7cn0KIyBDb250cm9sIHBvaW50cyBjb2xvciBieSBncm91cHMKZ2dwbG90KGRfZXhwLCBhZXMoeD1LTEY0LCB5PVJVTlgxKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKQoKIyBDaGFuZ2UgdGhlIGRlZmF1bHQgY29sb3IgbWFudWFsbHkuCiMgVXNlIHRoZSBzY2FsZV9jb2xvcl9tYW51YWwoKSBmdW5jdGlvbgpwIDwtIGdncGxvdChkX2V4cCwgYWVzKHg9S0xGNCwgeT1SVU5YMSkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU2FtcGxlKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwgIiNBMkFGQkIiLCAiIzE3QjhBQiIsICIjM0Y0RTc3IiwgIiNGQ0ZBMjciLCAiI0JGQUZGQiIsICIjNjlCODlCIiwgIiM3RjRFOTciKSkKcHJpbnQocCkKYGBgCgojIyBHR1Bsb3QgdGhlbWUKCk5vdGUgdGhhdCwgdGhlIGRlZmF1bHQgdGhlbWUgb2YgZ2dwbG90cyBpcyBgdGhlbWVfZ3JheSgpYCAob3IgYHRoZW1lX2dyZXkoKWApLCB3aGljaCBpcyB0aGVtZSB3aXRoIGdyZXkgYmFja2dyb3VuZCBhbmQgd2hpdGUgZ3JpZCBsaW5lcy4gTW9yZSB0aGVtZXMgYXJlIGF2YWlsYWJsZSBmb3IgcHJvZmVzc2lvbmFsIHByZXNlbnRhdGlvbnMgb3IgcHVibGljYXRpb25zLiBUaGVzZSBpbmNsdWRlOiBgdGhlbWVfYncoKWAsIGB0aGVtZV9jbGFzc2ljKClgIGFuZCBgdGhlbWVfbWluaW1hbCgpYC4KClRvIGNoYW5nZSB0aGUgdGhlbWUgb2YgYSBnaXZlbiBnZ3Bsb3QgKHApLCB1c2UgdGhpczogYHAgKyB0aGVtZV9jbGFzc2ljKClgLiAKCmBgYHtyfQpwIDwtIGdncGxvdChkX2V4cCwgYWVzKHg9S0xGNCwgeT1SVU5YMSkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU2FtcGxlKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwgIiNBMkFGQkIiLCAiIzE3QjhBQiIsICIjM0Y0RTc3IiwgIiNGQ0ZBMjciLCAiI0JGQUZGQiIsICIjNjlCODlCIiwgIiM3RjRFOTciKSkKcCA8LSBwICsgdGhlbWVfY2xhc3NpYygpCnByaW50KHApCgpgYGAKYGBge3J9CmRmIDwtIHJlZHVjZWREaW0oc2NlKQpoZWFkKGRmKQpgYGAKCiMjIEFkZGluZyBsYXllcnMgdG8gZ2dwbG90LCBMaW5lcyAoUHJlZGljdGlvbiBMaW5lKQpBIHBsb3QgY29uc3RydWN0ZWQgd2l0aCBnZ3Bsb3QgY2FuIGhhdmUgbW9yZSB0aGFuIG9uZSBnZW9tLiBJbiB0aGF0IGNhc2UgdGhlIG1hcHBpbmdzIGVzdGFibGlzaGVkIGluIHRoZSBgZ2dwbG90KClgIGNhbGwgYXJlIHBsb3QgZGVmYXVsdHMgdGhhdCBjYW4gYmUgYWRkZWQgdG8gb3Igb3ZlcnJpZGRlbi4gT3VyIHBsb3QgY291bGQgdXNlIGEgcmVncmVzc2lvbiBsaW5lOgpgYGB7cn0KZF9leHAkcHJlZC5TQyA8LSBwcmVkaWN0KGxtKFJVTlgxIH4gS0xGNCwgZGF0YSA9IGRfZXhwKSkKCmdncGxvdChkX2V4cCwgYWVzKHggPSBLTEY0LCB5ID0gUlVOWDEpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU2FtcGxlKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHByZWQuU0MpKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKCiMjIGBUaXRsZWAsIGB4bGFiYCAmIGB5bGFiYApgYGB7cn0KZGYgPC0gYXMuZGF0YS5mcmFtZShyZWR1Y2VkRGltKHNjZSkpCmRmJFNhbXBsZSA8LSBjb2xEYXRhKHNjZSkkU2FtcGxlCnAgPC0gZ2dwbG90KGRmLCBhZXMoeD1WMSwgeT1WMikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNCwgYWVzKGNvbG9yID0gU2FtcGxlKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwgIiNBMkFGQkIiLCAiIzE3QjhBQiIsICIjM0Y0RTc3IiwgIiNGQ0ZBMjciLCAiI0JGQUZGQiIsICIjNjlCODlCIiwgIiM3RjRFOTciKSkgKyAKICBnZ3RpdGxlKCd0LVNORSBwbG90IGZvciBTYW1wbGVzJykgKyAKICB4bGFiKCd0U05FLTEnKSArIAogIHlsYWIoJ3RTTkUtMicpICsgCiAgdGhlbWVfY2xhc3NpYygpCgpwCmBgYAoKYGBge3J9CmRmIDwtIGFzLmRhdGEuZnJhbWUocmVkdWNlZERpbShzY2UpKQpkZiRTYW1wbGUgPC0gY29sRGF0YShzY2UpJFNhbXBsZQpwIDwtIGdncGxvdChkZiwgYWVzKHg9VjEsIHk9VjIpKSsKICBnZW9tX3BvaW50KHNpemUgPSAwLjQsIGFlcyhjb2xvciA9IFNhbXBsZSkpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIsICIjQTJBRkJCIiwgIiMxN0I4QUIiLCAiIzNGNEU3NyIsICIjRkNGQTI3IiwgIiNCRkFGRkIiLCAiIzY5Qjg5QiIsICIjN0Y0RTk3IikpICsgCiAgZ2d0aXRsZSgndC1TTkUgcGxvdCBmb3IgU2FtcGxlcycpICsgCiAgeGxhYigndFNORS0xJykgKyAKICB5bGFiKCd0U05FLTInKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTQpKSkKCnAKYGBgCiMjIEhpc3RvZ3JhbQoKYGBge3J9CmdncGxvdChkX2V4cCwgYWVzKHg9dG90YWxfY291bnRzKSkgKyBnZW9tX2hpc3RvZ3JhbSgpICsgdGhlbWVfY2xhc3NpYygpIApgYGAKCgojIyBEZW5zaXR5IHBsb3QKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoeD1sb2cxMChzY2UkdG90YWxfY291bnRzKzEpLCBTYW1wbGUgPSBzY2UkU2FtcGxlKQpnZ3Bsb3QoZGYsCiAgICAgICBhZXMoeCA9IHgsIGZpbGwgPSBhcy5mYWN0b3IoU2FtcGxlKSkpICsgCiAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICAgICAgIGxhYnMoeCA9IGV4cHJlc3Npb24oJ2xvZydbMTBdKicoTGlicmFyeSBTaXplKScpLCB0aXRsZSA9ICJUb3RhbCByZWFkcyBkZW5zaXR5IiwgZmlsbCA9ICJTYW1wbGUiKSArIAogICAgICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAjIFNldHRpbmcgdGhlIGJhc2Ugc2l6ZSB0ZXh0IGZvciBwbG90cwogICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCAiI0EyQUZCQiIsICIjMTdCOEFCIiwgIiMzRjRFNzciLCAiI0ZDRkEyNyIsICIjQkZBRkZCIiwgIiM2OUI4OUIiLCAiIzdGNEU5NyIpKSAjIE5lZWQgdG8gc2V0IGZpbGwgbWFudWFsCmBgYAoKIyMgRmFjZXQKCmBgYHtyfQpkZiA8LSBkYXRhLmZyYW1lKHg9bG9nMTAoc2NlJHRvdGFsX2NvdW50cysxKSwgU2FtcGxlID0gc2NlJFNhbXBsZSkKZ2dwbG90KGRmLAogICAgICAgYWVzKHggPSB4LCBmaWxsID0gYXMuZmFjdG9yKFNhbXBsZSkpKSArIAogICAgICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArCiAgICAgICBsYWJzKHggPSBleHByZXNzaW9uKCdsb2cnWzEwXSonKExpYnJhcnkgU2l6ZSknKSwgdGl0bGUgPSAiVG90YWwgcmVhZHMgZGVuc2l0eSIsIGZpbGwgPSAiU2FtcGxlIikgKyAKICAgICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgIyBTZXR0aW5nIHRoZSBiYXNlIHNpemUgdGV4dCBmb3IgcGxvdHMKICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwgIiNBMkFGQkIiLCAiIzE3QjhBQiIsICIjM0Y0RTc3IiwgIiNGQ0ZBMjciLCAiI0JGQUZGQiIsICIjNjlCODlCIiwgIiM3RjRFOTciKSkgKyAgIyBOZWVkIHRvIHNldCBmaWxsIG1hbnVhbCAKICBmYWNldF93cmFwKH5TYW1wbGUpCmBgYAoKIyBTdGF0aXN0aWNhbCBUcmFuc2Zvcm1hdGlvbnMKIyMgU3RhdGlzdGljYWwgVHJhbnNmb3JtYXRpb25zClNvbWUgcGxvdCB0eXBlcyAoc3VjaCBhcyBzY2F0dGVycGxvdHMpIGRvIG5vdCByZXF1aXJlIHRyYW5zZm9ybWF0aW9uc+KAk2VhY2ggcG9pbnQgaXMgcGxvdHRlZCBhdCB4IGFuZCB5IGNvb3JkaW5hdGVzIGVxdWFsIHRvIHRoZSBvcmlnaW5hbCB2YWx1ZS4gT3RoZXIgcGxvdHMsIHN1Y2ggYXMgYm94cGxvdHMsIGhpc3RvZ3JhbXMsIHByZWRpY3Rpb24gbGluZXMgZXRjLiByZXF1aXJlIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uczoKCi0gZm9yIGEgc21vb3RoZXIgc21vdGhlciB0aGUgeSB2YWx1ZXMgbXVzdCBiZSB0cmFuc2Zvcm1lZCBpbnRvIHByZWRpY3RlZCB2YWx1ZXMKCmBgYHtyfQpkX2V4cCRwcmVkLlNDIDwtIHByZWRpY3QobG0oUlVOWDEgfiBLTEY0LCBkYXRhID0gZF9leHApKQoKZ2dwbG90KGRfZXhwLCBhZXMoeCA9IEtMRjQsIHkgPSBSVU5YMSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CmRfZXhwJHByZWQuU0MgPC0gcHJlZGljdChsbShSVU5YMSB+IEtMRjQsIGRhdGEgPSBkX2V4cCkpCgpnZ3Bsb3QoZF9leHAsIGFlcyh4ID0gS0xGNCwgeSA9IFJVTlgxKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNhbXBsZSkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKCgoK